package com.rankeiot.core.sqltoy;

import com.rankeiot.core.util.StringUtil;
import org.sagacity.sqltoy.config.annotation.Column;
import org.sagacity.sqltoy.config.annotation.Entity;

import java.lang.invoke.SerializedLambda;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author desire
 */
@lombok.Getter
public class ColumnInfo {

    private static final Map<String, WeakReference<ColumnInfo>> FIELD_CACHE = new ConcurrentHashMap<>();

    public static String getTableName(Class tableEntity) {
        Entity enn = (Entity) tableEntity.getAnnotation(Entity.class);
        if (enn == null) {
            return tableEntity.getSimpleName();
        } else {
            return enn.tableName();
        }
    }

    public static <T, R> String getColumnName(Getter<T, R> column) {
        ColumnInfo info = getColumnInfo(column);
        if (info != null) {
            return info.getColumn();
        }
        return null;
    }

    public static <T, R> ColumnInfo getColumnInfo(Getter<T, R> column) {
        String canonicalName = column.getClass().toString();
        return Optional.ofNullable(FIELD_CACHE.get(canonicalName))
                .map(WeakReference::get)
                .orElseGet(() -> {
                    ColumnInfo v = getColumnInfo0(column);
                    FIELD_CACHE.put(canonicalName, new WeakReference<>(v));
                    return v;
                });
    }

    private static ColumnInfo getColumnInfo0(Getter column) {
        SerializedLambda lambda = getSerializedLambda(column);
        if (lambda == null) {
            return null;
        }
        String field = StringUtil.firstCharToLowerCase(lambda.getImplMethodName().replaceFirst("get", ""));
        String tableColumn = field;
        Column columnAnno = null;
        Field jfield = null;
        try {
            String className = lambda.getImplClass().replaceAll("/", ".");
            Class domain = Class.forName(className, false, ColumnInfo.class.getClassLoader());
            jfield = domain.getDeclaredField(field);
            columnAnno = jfield.getAnnotation(Column.class);
            tableColumn = columnAnno.name();

        } catch (NoSuchFieldException | ClassNotFoundException e) {
            e.printStackTrace();
        }
        return new ColumnInfo(field, tableColumn, columnAnno);
    }

    /**
     * 获取Getter的
     */
    private static SerializedLambda getSerializedLambda(Getter getter) {
        if (!getter.getClass().isSynthetic()) {
            throw new RuntimeException("该方法仅能传入 lambda 表达式产生的合成类");
        }
        try {
            Method method = getter.getClass().getDeclaredMethod("writeReplace");
            method.setAccessible(Boolean.TRUE);
            SerializedLambda lambda = (SerializedLambda) method.invoke(getter);
            return lambda;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    private String field;
    private String column;
    private Column info;

    protected ColumnInfo(String field, String column, Column info) {
        this.field = field;
        this.column = column;
        this.info = info;
    }
}
